////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNMemory/inc/WNMemory.h"
#include "WNMath/inc/WNPlane.h"
#include "WNMath/inc/WNExtended.h"
#include "WNMath/inc/WNPoint3.h"
#include "WNMath/inc/WNVector3.h"

using namespace WNMath;
using namespace WNMemory;

// Constructors ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

WNPlane::WNPlane() :
    a(0.0f),
    b(0.0f),
    c(0.0f),
    d(0.0f) {
}

WNPlane::WNPlane(const WN_FLOAT32* _f) :
    a(_f[0]),
    b(_f[1]),
    c(_f[2]),
    d(_f[3]) {
}

WNPlane::WNPlane(const WNVector3& _normal, WN_FLOAT32 _distance) :
    a(_normal.x),
    b(_normal.y),
    c(_normal.z),
    d(-_distance) {
}

WNPlane::WNPlane(WN_FLOAT32 _a, WN_FLOAT32 _b, WN_FLOAT32 _c, WN_FLOAT32 _d) :
    a(_a),
    b(_b),
    c(_c),
    d(_d) {
}

// Operators //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

WNPlane::operator WN_FLOAT32* () {
    return(&a);
}

WNPlane::operator const WN_FLOAT32* () const {
    return(static_cast<const WN_FLOAT32*>(&a));
}

WNPlane& WNPlane::operator += (const WNPlane& _plane) {
    a += _plane.a;
    b += _plane.b;
    c += _plane.c;
    d += _plane.d;

    return(*this);
}

WNPlane& WNPlane::operator -= (const WNPlane& _plane) {
    a -= _plane.a;
    b -= _plane.b;
    c -= _plane.c;
    d -= _plane.d;

    return(*this);
}

WNPlane WNPlane::operator + () const {
    return(*this);
}

WNPlane WNPlane::operator - () const {
    return(WNPlane(-a, -b, -c, -d));
}

WNPlane WNPlane::operator + (const WNPlane& _plane) const {
    return(WNPlane(a + _plane.a, b + _plane.b, c + _plane.c, d + _plane.d));
}

WNPlane WNPlane::operator - (const WNPlane& _plane) const {
    return(WNPlane(a - _plane.a, b - _plane.b, c - _plane.c, d - _plane.d));
}

WN_BOOL WNPlane::operator == (const WNPlane& _plane) const {
    return(0 == WNMemCmp(this, &_plane, sizeof(WNPlane)));
}

WN_BOOL WNPlane::operator != (const WNPlane& _plane) const {
    return(0 != WNMemCmp(this, &_plane, sizeof(WNPlane)));
}

// Members ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

WN_VOID WNPlane::Zero() {
    WNMemClr(&a, sizeof(WNPlane));
}

WN_VOID WNPlane::Set(const WN_FLOAT32* _f) {
    WNMemCpy(&a, &_f[0], sizeof(WNPlane));
}

WN_VOID WNPlane::Set(WN_FLOAT32 _a, WN_FLOAT32 _b, WN_FLOAT32 _c, WN_FLOAT32 _d) {
    a = _a;
    b = _b;
    c = _c;
    d = _d;
}

WN_VOID WNPlane::Define(const WNPoint3& _point1, const WNPoint3& _point2, const WNPoint3& _point3) {
    const WNVector3 normal = WNCross((_point1 - _point2), (_point3 - _point2));

    a = normal.x;
    b = normal.y;
    c = normal.z;

    const WNVector3 vec = WNVector3(_point2.x, _point2.y, _point2.z);

    d = -(WNDot(Normal(), vec));
}

WN_VOID WNPlane::Define(const WNVector3& _vector1, const WNVector3& _vector2, const WNVector3& _vector3) {
    const WNVector3 normal = WNCross(_vector1, _vector2);

    a = normal.x;
    b = normal.y;
    c = normal.z;

    d = -(WNDot(Normal(), _vector3));
}

WN_VOID WNPlane::Normalize() {
    WNVector3 vec = WNVector3(a, b, c);

    vec.Normalize();

    a = vec.x;
    b = vec.y;
    c = vec.z;
}

WN_VOID WNPlane::Translate(const WNVector3& _translation) {
    d -= WNDot(_translation, Normal());
}

WNVector3 WNPlane::Normal() const {
    return(WNVector3(a, b, c));
}

WN_FLOAT32 WNPlane::Distance() const {
    return(-d);
}

WNPlane WNPlane::GetNormalized() const {
    WNPlane pln = *this;

    return(pln.Normalize(), pln);
}

WNPlane WNPlane::GetTranslated(const WNVector3& _translation) const {
    WNPlane pln = *this;

    return(pln.Translate(_translation), pln);
}